<?php

declare(strict_types=1);

namespace Tool\Command;

use Marx\Console\Color;
use Marx\Console\Command\AbstractCommand;
use Tool\Database\AbstractColumn;
use Tool\Database\ColumnFactory;
use Tool\Database\Connection;
use Tool\Database\DSN;
use Tool\Database\Table;

/**
 * 数据表模拟数据工具.
 */
class MockCommand extends AbstractCommand
{
    public function __construct()
    {
        $this->setName('mock');
        $this->setDesc("mock table's data");
        $this->addOption('dbname', 'd', true, 'database name');
        $this->addOption('user', 'u', true, 'user name');
        $this->addOption('pwd', 'p', true, 'user password');
        $this->addOption('host', '', false, 'database host,default localhost', 'localhost');
        $this->addOption('port', '', false, 'database port,default 3306', '3306');
        $this->addOption('charset', 'c', false, 'database charset,default utf8', 'utf8');
    }

    public function execute(): void
    {
        $dbName = $this->input->getOption('dbname');

        $dsn = new DSN();
        $dsn->setHost($this->input->getOption('host'));
        $dsn->setPort($this->input->getOption('port'));
        $dsn->setDbName($dbName);
        $dsn->setCharset($this->input->getOption('charset'));

        $user = $this->input->getOption('user');
        $pwd = $this->input->getOption('pwd');
        $conn = new Connection($dsn, $user, $pwd);
        $table = new Table($conn);

        while (true) {
            // 要mock数据的表名
            $this->output->askQuestion('please input table name,or input empty string to exit');
            $tableName = $this->input->readAnswer();
            if (!$tableName) {
                break;
            }

            // 字段mock类
            $columnArr = $table->getColumnArr($dbName, $tableName);
            $classList = ColumnFactory::getClassList();
            foreach ($columnArr as &$column) {
                $defaultIndex = ColumnFactory::getDefaultClass($column['DATA_TYPE']);
                $askClassList = [];
                foreach ($classList as $index => $itemName) {
                    $itemName = "[{$index}] {$itemName}";
                    $askClassList[] = $index === intval($defaultIndex) ? Color::green($itemName) : $itemName;
                }
                $this->output->askQuestion('', '', 'please choose column "'.$column['COLUMN_NAME'].'" class index['.Color::yellow($defaultIndex).']', ...$askClassList);
                while (true) {
                    $classIndex = $this->input->readAnswer();
                    $className = ColumnFactory::getClassName($classIndex, $defaultIndex);
                    if (is_null($className)) {
                        $this->output->askQuestion(Color::red('Error:').'"'.$classIndex.'" is illegal index,please re-enter');

                        continue;
                    }

                    /** @var AbstractColumn $mock */
                    $mock = new $className($column);
                    $question = $mock->askExtArgs();
                    if ($question) {
                        $this->output->askQuestion('', $question);
                        $mock->setExtArgs($this->input->readAnswerAsArray() ?? []);
                    }
                    $column['mock'] = $mock;

                    break;
                }
            }

            // 截断表
            $table->truncateTable($tableName);

            // 生成多少条
            $this->output->askQuestion('', '', Color::red('how many rows do you want to create').'['.Color::yellow('100').']');
            $count = intval($this->input->readAnswer() ?: 100);

            // 分批批量插入
            $maxSingleCount = 10000;
            $data = [];
            $start = time();
            for ($i = 0; $i < $count; ++$i) {
                $columnData = [];
                foreach ($columnArr as &$column) {
                    /** @var AbstractColumn $mock */
                    $mock = $column['mock'];
                    $columnData[$column['COLUMN_NAME']] = $mock->getData();
                }
                $data[] = $columnData;

                $isLast = $i + 1 === $count;
                if ($maxSingleCount === count($data) || $isLast) {
                    $table->batchInsert($tableName, $columnArr, $data);
                    $data = [];

                    $percent = number_format(100 * ($i + 1) / $count, 2, '.', null);
                    $this->output->write('progress: '.Color::grey("{$percent}%")." \r", $isLast);
                }
            }

            $this->output->writeLn(Color::green('mock success').',total seconds:'.(time() - $start), '');
        }
    }
}
